﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.ServiceModel;
using Quail.BinaryCompressedPackageFile;
using QuailDb;

namespace QuailDashboard.View.Web
{
    public partial class QuailTestRunner
    {
        /// <summary>
        /// Gets all the files for a binary package
        /// </summary>
        /// <param name="packagename">The name of the package</param>
        /// <param name="version">(optional) the version number.  If null, the latest version is returned</param>
        /// <returns>A list of files for the binary package</returns>
        [OperationContract]
        public List<BinaryCompressedPackageFile> GetBinaryPackageFiles(string packagename, string version)
        {
            var bp = GetBinaryPackage(packagename, version).Single();
            return bp.BinaryPackageFiles
                .Select(bpf => bpf.BinaryFile)
                .Select(bf => new BinaryCompressedPackageFile(bf.Filename, bf.FileHash, bf.BinaryData.ToArray()))
                .ToList();
        }

        /// <summary>
        /// Deletes a binary package
        /// </summary>
        /// <param name="packagename">the name of the package</param>
        /// <param name="version">the version number of the package</param>
        [OperationContract]
        public void DeleteBinaryPackage(string packagename, string version)
        {
            var bp = GetBinaryPackage(packagename, version).Single();
            bp.BinaryPackageFiles.Load();
            _db.BinaryPackageFiles.DeleteAllOnSubmit(bp.BinaryPackageFiles);
            _db.BinaryPackages.DeleteOnSubmit(bp);
            _db.SubmitChanges();
        }

        /// <summary>
        /// Creates a binary package for the given package name and version.  If the package and version already exists, it will use it
        /// instead, and remove any existing files for the package.  Call AddBinaryPackageFile() to add files to the package.
        /// </summary>
        /// <param name="packagename">The name of the package</param>
        /// <param name="version">The version # of the package</param>
        /// <returns>The packageid</returns>
        [OperationContract]
        public int CreateBinaryPackage(string packagename, string version)
        {
            var entities = GetBinaryPackage(packagename, version).ToList();
            BinaryPackage bp;

            if (entities.Count == 0)
            {
                bp = new BinaryPackage
                {
                    PackageName = packagename,
                    LastUpdateDate = DateTime.Now,
                    Version = version
                };
                _db.BinaryPackages.InsertOnSubmit(bp);
            }
            else
            {
                bp = entities[0];
                bp.BinaryPackageFiles.Load();
                bp.LastUpdateDate = DateTime.Now;
                _db.BinaryPackageFiles.DeleteAllOnSubmit(bp.BinaryPackageFiles);
            }

            _db.SubmitChanges();

            return bp.BinaryPackageId;
        }

        /// <summary>
        /// Checks to see if the binary file needs to be added to the package
        /// </summary>
        /// <param name="packageid">The packageid returned from CreateBinaryPackage</param>
        /// <param name="binaryfile">The binary file info (no binary data)</param>
        /// <returns>True if the binary file needs to be added</returns>
        [OperationContract]
        public bool CheckAddBinaryPackageFile(int packageid, BinaryFileInfo binaryfile)
        {
            var bp = _db.BinaryPackages.Where(bpkg => bpkg.BinaryPackageId == packageid).Single();
            var filename = Path.GetFileName(binaryfile.Filename);
            var binarymatches = _db.BinaryFiles.Where(binfile => binfile.FileHash == binaryfile.FileHash && binfile.Filename == filename);

            if (binarymatches.Count() == 0)
                return true;

            var bf = binarymatches.Single();

            var matches = _db.BinaryPackageFiles.Where(bpfs => bpfs.BinaryPackageId == bp.BinaryPackageId && bpfs.BinaryFile.Filename == filename);

            if (matches.Count() == 0)
            {
                new BinaryPackageFile
                {
                    LastUpdateDate = DateTime.Now,
                    BinaryFile = bf,
                    BinaryPackage = bp
                };
            }
            else
            {
                matches.Single().BinaryFile = bf;
            }

            _db.SubmitChanges();

            return false;
        }

        /// <summary>
        /// Adds a binary file to the binary package created in a call to CreateBinaryPackage()
        /// </summary>
        /// <param name="packageid">The packageid returned from CreateBinaryPackage</param>
        /// <param name="binaryfile">The binary file</param>
        /// <returns>The BinaryFileId</returns>
        [OperationContract]
        public int AddBinaryPackageFile(int packageid, BinaryCompressedPackageFile binaryfile)
        {
            var bp = _db.BinaryPackages.Where(bpkg => bpkg.BinaryPackageId == packageid).Single();
            var filename = Path.GetFileName(binaryfile.Filename);
            var binarymatches = _db.BinaryFiles.Where(binfile => binfile.FileHash == binaryfile.Hash && binfile.Filename == filename);


            BinaryFile bf;
            BinaryPackageFile bpf;

            if (binarymatches.Count() == 0)
            {
                bf = new BinaryFile
                {
                    LastUpdateDate = DateTime.Now,
                    BinaryData = binaryfile.BinaryData,
                    FileHash = binaryfile.Hash,
                    Filename = filename
                };

                _db.BinaryFiles.InsertOnSubmit(bf);
            }
            else
                bf = binarymatches.Single();

            var matches = _db.BinaryPackageFiles.Where(bpfs => bpfs.BinaryPackageId == bp.BinaryPackageId && bpfs.BinaryFile.Filename == filename);

            if (matches.Count() == 0)
            {
                bpf = new BinaryPackageFile
                {
                    LastUpdateDate = DateTime.Now,
                    BinaryFile = bf,
                    BinaryPackage = bp
                };
            }
            else
            {
                bpf = matches.Single();
                bpf.BinaryFile = bf;
            }

            _db.SubmitChanges();

            return bpf.BinaryFileId;
        }

        /// <summary>
        /// Closes the binary package, making sure resources used are cleaned up.
        /// </summary>
        /// <param name="packageid">The packageid returned from CreateBinaryPackage</param>
        [OperationContract]
        public void FinalizeBinaryPackage(int packageid)
        {
            
        }

        /// <summary>
        /// Gets a list of rows from the BinaryFiles table.  The BinaryData column is not returned, use GetBinaryFile to get the binary file for it.
        /// </summary>
        /// <returns>A list of BinaryFileInfo objects</returns>
        [OperationContract]
        public List<BinaryFileInfo> GetBinaryFiles()
        {
            return _db.BinaryFiles
                .Select(bf => new BinaryFileInfo
                {
                    BinaryFileId = bf.BinaryFileId,
                    FileHash = bf.FileHash,
                    Filename = bf.Filename,
                    LastUpdateDate = bf.LastUpdateDate
                })
                .ToList();
        }

        /// <summary>
        /// Gets a BinaryCompressedPackageFile instance for the BinaryFileId.  Use GetBinaryFiles() to get a list of all available files
        /// </summary>
        /// <param name="binaryfileid">the BinaryFileId of the file you want to get</param>
        /// <returns>A BinaryCompressedPackageFile containing the compressed image of the file</returns>
        [OperationContract]
        public BinaryCompressedPackageFile GetCompressedBinaryFile(int binaryfileid)
        {
            return _db.BinaryFiles
                .Where(bf => bf.BinaryFileId == binaryfileid)
                .Select(bf => new BinaryCompressedPackageFile(bf.Filename, bf.FileHash, bf.BinaryData.ToArray()))
                .Single();
        }

		[OperationContract]
		public List<BinaryPackageInfo> GetBinaryPackages()
		{
			return _db.BinaryPackages
				.Select(bp => new BinaryPackageInfo
				              	{
				              		LastUpdated = bp.LastUpdateDate,
				              		PackageName = bp.PackageName,
				              		PackageVersion = bp.Version
				              	})
				.ToList();
		}

        /// <summary>
        /// Gets a list of BinaryPackages that may contain a given assembly
        /// </summary>
        /// <param name="assemblies"></param>
        /// <returns></returns>
        [OperationContract]
        public List<BinaryPackageInfo> GetBinaryPackagesForAssemblies(List<string> assemblies)
        {
            var result = new List<BinaryPackageInfo>();

            foreach (var asm in assemblies)
            {
                var asmname = asm;
                var bfids = _db.BinaryFiles
                    .Where(bf => string.Compare(bf.Filename, asmname, true) == 0)
                    .Select(bf => bf.BinaryFileId);
                var bpfs = _db.BinaryPackageFiles
                    .Join(bfids, bpf => bpf.BinaryFileId, bfid => bfid, (bpf, bfid) => bpf);
                var bps = _db.BinaryPackages
                    .Join(bpfs, bp => bp.BinaryPackageId, bpf => bpf.BinaryPackageId,
                          (bp, bpf) => new BinaryPackageInfo
                                           {
                                               PackageName = bp.PackageName, PackageVersion = bp.Version, LastUpdated = bp.LastUpdateDate
                                           });

                foreach (var bp in bps)
                {
                    var lbp = bp;
                    if (!result.Any(bpi => string.Compare(bpi.PackageName, lbp.PackageName) == 0 && bpi.PackageVersion == lbp.PackageVersion))
                        result.Add(lbp);
                }
            }

            Comparison<BinaryPackageInfo> comp = (l, r) =>
                                                     {
                                                         if (l.LastUpdated > r.LastUpdated)
                                                             return -1;

                                                         if (l.LastUpdated == r.LastUpdated)
                                                             return 0;
                                                         
                                                         return 1;
                                                     };
            result.Sort(comp);
            return result;
        }



        private IEnumerable<BinaryPackage> GetBinaryPackage(string packagename, string version)
        {
            return _db.BinaryPackages.Where(pack => pack.PackageName == packagename && pack.Version == version).ToList();
        }
    }
}
